package com.marvels.approve.manager.service.impl;

import com.marvels.approve.manager.dao.ICaseManagerDao;
import com.marvels.approve.manager.dao.ICaseManagerVersionDao;
import com.marvels.approve.manager.dto.ActivitiNodeDTO;
import com.marvels.approve.manager.dto.CaseManagerDTO;
import com.marvels.approve.manager.dto.CaseManagerVersionDTO;
import com.marvels.approve.manager.service.IActivityLoadingService;
import com.marvels.approve.process.dto.CompleteProcessDTO;
import com.marvels.approve.process.dto.ProcessApproveDTO;
import com.marvels.common.constants.CaseNodeConstant;
import com.marvels.common.dto.UserDTO;
import com.marvels.common.exception.MarvelsBusinessException;
import com.marvels.common.utils.*;
import org.activiti.bpmn.model.FlowNode;
import org.activiti.bpmn.model.SequenceFlow;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.impl.persistence.entity.ExecutionEntity;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.DeploymentBuilder;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.activiti.engine.task.TaskQuery;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.File;
import java.io.FileInputStream;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Service
public class ActivityLoadingServiceImpl implements IActivityLoadingService {
	@Autowired  
	private RepositoryService repositoryService; 
	@Autowired  
	private RuntimeService runtimeService;  
	@Autowired  
	private TaskService taskService;  
	@Autowired  
	private ICaseManagerDao caseManagerDao;
	@Autowired  
	private ICaseManagerVersionDao caseManagerVersionDao;
	@Value("${bing.process.file.path}")
	private String processFilePath;
	@Transactional(timeout = 30 ,rollbackFor=Exception.class)
	public void loadProcessFile(String filePath)throws MarvelsBusinessException{
		//检查流程是否存在
		String checkMD5Result = checkMD5(filePath);
		CaseManagerDTO caseManagerDTO = new CaseManagerDTO();
		caseManagerDTO.setEnCode(checkMD5Result);
		List flows = caseManagerDao.select(caseManagerDTO);
		if(flows!=null && flows.size()>0){
			throw new MarvelsBusinessException("流程无更改，不再重复加载!"+filePath);
		}
		//创建流程定义
		CaseManagerDTO caseManager = createProcessDefinition(checkMD5Result,filePath);
		//创建更新流程管理
		createCaseManager(caseManager,filePath);
	}
	/**创建流程定义
	 * @author 何杰
	 * 2018年4月16日
	 * @param checkMD5Result
	 * @param filePath
	 * @return
	 */
	@Transactional(timeout = 30 ,rollbackFor=Exception.class)
	private CaseManagerDTO createProcessDefinition(String checkMD5Result,String filePath)throws MarvelsBusinessException{
		CaseManagerDTO caseManager = new CaseManagerDTO();
		caseManager.setEnCode(checkMD5Result);
		//加载流程
		DeploymentBuilder builder = repositoryService.createDeployment();
		// 加载流程定义文件
        builder.addClasspathResource("processes/"+filePath);
        builder.addClasspathResource("processes/"+filePath.replace(".bpmn", ".png"));
		//根据bpmn文件部署流程
		Deployment deployment = builder.deploy();
		//获取流程定义
		ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().deploymentId(deployment.getId()).singleResult();
		
		if(processDefinition==null){
			throw new MarvelsBusinessException("获取流程定义失败"+filePath);
		}
		
		String processDefinitionId = processDefinition.getId();
		String key = processDefinition.getKey();
		String name = processDefinition.getName();
		int version = processDefinition.getVersion();
		
		String category = processDefinition.getCategory();
		String resourceName = processDefinition.getResourceName();
		String tranId = processDefinition.getTenantId();
		
		caseManager.setProcessDefinitionId(processDefinitionId);
		caseManager.setActKey(key);
		caseManager.setActName(name);
		caseManager.setActVersion(version);
		caseManager.setName(name);
		//获取流程第一个节点
		caseManager.setFirstNode(getFirstNodes(caseManager));
		return caseManager;
	}
	
	public String getFirstNodes(CaseManagerDTO caseManager) throws MarvelsBusinessException{
		StringBuffer nodes = new StringBuffer();
		java.util.Collection<org.activiti.bpmn.model.FlowElement> fles = this.findFlowElements(caseManager.getProcessDefinitionId());
		if(fles==null || fles.size() <2){
			throw new MarvelsBusinessException(caseManager.getActKey()+"流程有异常，请联系研发！");
		}
		String firstNodeId = "";
		for(org.activiti.bpmn.model.FlowElement fe:fles){
			String nodeId = fe.getId();
			if("woStart".equals(nodeId) && fe instanceof FlowNode){
				//所有
				List<SequenceFlow> list = ((FlowNode)fe).getOutgoingFlows();
				int size = list.size();
				for (int i=0;i<size;i++) {
					SequenceFlow sf = list.get(i);
					String nodeCode = sf.getTargetRef();
					if(i<size-1){
						nodes.append(nodeCode).append(",");
					}else{
						nodes.append(nodeCode);
					}
				}
				break;
			}
		}
		return nodes.toString();
	}
	/**创建更新流程
	 * @author 何杰
	 * 2018年4月16日
	 * @param caseManager
	 * @return
	 */
	@Transactional(timeout = 30 ,rollbackFor=Exception.class)
	private void createCaseManager(CaseManagerDTO caseManager,String filePath)throws MarvelsBusinessException{
		CaseManagerDTO caseManagerDTO = new CaseManagerDTO();
		caseManagerDTO.setActKey(caseManager.getActKey());
		caseManagerDTO.setStatus(0);
		List<CaseManagerDTO> flowEntitys = caseManagerDao.select(caseManagerDTO);
		
		if(flowEntitys!=null && flowEntitys.size()> 0){//更新流程
			CaseManagerDTO entity = flowEntitys.get(0);
			entity.setActVersion(caseManager.getActVersion());
			entity.setEnCode(caseManager.getEnCode());
			UserDTO user = CacheUtils.getCacheUser();
			entity.setUpdatedBy(user.getUserName());
			entity.setUpdatedDate(new Date());
			entity.setProcessDefinitionId(caseManager.getProcessDefinitionId());
			entity.setFirstNode(caseManager.getFirstNode());
			this.caseManagerDao.updateByPrimaryKeySelective(entity);
		}else{//新增流程
			caseManager.setCode(MarvelsUtils.generateUUIDCode());
			caseManager.setStatus(0);
			this.caseManagerDao.insert(caseManager);
		}
		MarvelsLogUtil.getInstance().info((flowEntitys!=null && flowEntitys.size()> 0)+"init_caseManager......" + caseManager);
		//version 流水表
		CaseManagerVersionDTO versionEntity = new CaseManagerVersionDTO();
		versionEntity.setActKey(caseManager.getActKey());
		versionEntity.setActName(caseManager.getActName());
		versionEntity.setActVersion(caseManager.getActVersion());
		versionEntity.setEnCode(caseManager.getEnCode());
		versionEntity.setStatus(0);
		versionEntity.setResourceFile(filePath);

		caseManagerVersionDao.insert(versionEntity);
		MarvelsLogUtil.getInstance().info("init_versionEntity......" + versionEntity);
	}
	
	/**获取流程文件MD5值
	 * @author 何杰
	 * 2018年4月16日
	 * @param fileName
	 * @return
	 */
	private String checkMD5(String fileName)throws MarvelsBusinessException{
		try {
//			InputStream is = ActivityLoadingServiceImpl.class.getClassLoader().getSystemResourceAsStream("processes/"+fileName);
			FileInputStream fis = new FileInputStream(new File(processFilePath+"/processes/"+fileName));
			byte[] bytes = new byte[fis.available()];
			while (fis.read(bytes)>0) {
				fis.read(bytes);
			}
			String content = new String(bytes);
			String encryptStr = ShaUtils.Encrypt(content, "MD5");
			return encryptStr;
		} catch (Exception e) {
			MarvelsLogUtil.getInstance().error(processFilePath+"/"+fileName,e);
			throw new MarvelsBusinessException(processFilePath+"/"+fileName+"流程文件找不到");
		}
	}

	/**
	 * @author 何杰
	 * 2018年4月16日
	 * @param actKey
	 * @param useId
	 * @return
	 * @throws MarvelsBusinessException
	 */
	@Override
	public String findProcess(String actKey,String useId) throws MarvelsBusinessException {
		// TODO 自动生成的方法存根 
		Map<String, Object> variables = new HashMap<String,Object>();
	    variables.put("inputUser", useId);
		ProcessInstance pInstance = runtimeService.startProcessInstanceByKey(actKey,variables);
		MarvelsLogUtil.getInstance().info("流程实例ID:"+pInstance.getId()); 
		MarvelsLogUtil.getInstance().info("流程定义ID:"+pInstance.getProcessDefinitionId()); 
		return pInstance.getId();
	}

	@Override
	public java.util.Collection<org.activiti.bpmn.model.FlowElement> findFlowElements(String processDefinitionId) throws MarvelsBusinessException {
		org.activiti.bpmn.model.BpmnModel model = repositoryService.getBpmnModel(processDefinitionId);
		if(model != null) {  
			model.getMainProcess().getFlowElements();
			java.util.Collection<org.activiti.bpmn.model.FlowElement> flowElements = model.getMainProcess().getFlowElements();  
//		    for(org.activiti.bpmn.model.FlowElement e : flowElements) {  
//		        System.out.println("flowelement id:" + e.getId() + "  name:" + e.getName() + "   class:" + e.getClass().toString());  
//		    } 
			return flowElements;
		} 
		return null;
	}
	@Override
	public ActivitiNodeDTO startProcessInstanceByKey(String processDefinitionKey) throws MarvelsBusinessException {
		// 开启实例
		ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(processDefinitionKey);//使用流程定义的key启动流程实例，key对应X.bpmn文件
		if(processInstance==null){
			throw new MarvelsBusinessException("未找到相应流程,流程key:"+processDefinitionKey);
		}
		String processInstanceId = processInstance.getId();
//		ActivitiNodeDTO curNode = getCurNode(taskId);
//		List<Task> tasks = taskService.createTaskQuery().list();
		TaskQuery tq = taskService.createTaskQuery().processInstanceId(processInstanceId);
		Task task = tq.singleResult();
//		ExecutionEntity execution = (ExecutionEntity) runtimeService.createProcessInstanceQuery().processInstanceId(taskId).singleResult();  
//		TaskQuery tq2 =taskService.createTaskQuery().executionId(execution.getId());
//		Task xx2 = tq2.singleResult();
		ActivitiNodeDTO curNode = new ActivitiNodeDTO();
		curNode.setNodeCode(processInstance.getActivityId());
		curNode.setNodeName(task.getName());
		curNode.setProcessInstanceId(processInstanceId);
		curNode.setProcessTaskId(task.getId());
		return curNode;
	} 
	public Map getVariables(String taskId)throws MarvelsBusinessException{
		//通过taskid获取参数
		Map varis = taskService.getVariables(taskId);
		return varis;
	}
	/**获取taskId*/
	private String getTaskIdProcessInstanceByKey(String processDefinitionKey) throws MarvelsBusinessException {
		// 开启实例
		ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(processDefinitionKey);// 使用流程定义的key启动流程实例，key对应X.bpmn文件
		if (processInstance == null) {
			throw new MarvelsBusinessException("未找到相应流程,流程key:" + processDefinitionKey);
		}
		String taskId = processInstance.getId();
		ExecutionEntity execution = (ExecutionEntity) runtimeService.createProcessInstanceQuery().processInstanceId(taskId).singleResult();  
		List ids = execution.getTasks();
		return taskId;
	}
	@Transactional(timeout = 30 ,rollbackFor=Exception.class)
	public void completeTask(CompleteProcessDTO completeDto) throws MarvelsBusinessException {
		//完成当前节点
		if(com.marvels.common.utils.StringUtils.isBlank(completeDto.getProcessInstanceId()) || completeDto == null){
			throw new MarvelsBusinessException("流程实例ID为空");
		}
		if(completeDto.getCurNode()==null || com.marvels.common.utils.StringUtils.isBlank(completeDto.getCurNode().getNodeCode())) {
			throw new MarvelsBusinessException("当前节点不能为空，任务ID:"+completeDto.getProcessInstanceId());
		}
		ActivitiNodeDTO curNode = getCurNodeByProcessInstanceId(completeDto.getProcessInstanceId());
		if(curNode==null||!completeDto.getCurNode().getNodeCode().equals(curNode.getNodeCode())){
			//如果有原始节点，且跟流程中不一致
			throw new MarvelsBusinessException("流程实例ID："+completeDto.getProcessInstanceId()+"所处理的节点:"+completeDto.getCurNode()+"跟流程中相应节点不一致:"+curNode);
		}
		//设置流程参数
		ProcessApproveDTO processApproveDto  = completeDto.getProcessApproveDto();
		processApproveDto.setLoanId(completeDto.getLoanId());
		if(processApproveDto!=null){
			taskService.setVariables(curNode.getProcessTaskId(), BeanUtils.beanConverToMap(processApproveDto));
		}
		MarvelsLogUtil.getInstance().info("测试监听器》》》》》》》》》》》》》》》"+completeDto.getProcessInstanceId()+"--"+curNode.getNodeCode());
		taskService.complete(curNode.getProcessTaskId());
		//获取zhuan
		ActivitiNodeDTO changeNode = getCurNodeByProcessInstanceId(curNode.getProcessInstanceId());
		completeDto.setChangeNode(changeNode);
		
		MarvelsLogUtil.getInstance().info("完成任务:"+completeDto.getProcessInstanceId());
	}
	private ActivitiNodeDTO getCurNodeByProcessInstanceId(String processInstanceId) {
		TaskQuery tq = taskService.createTaskQuery().processInstanceId(processInstanceId);
		Task task = tq.singleResult();
//		ExecutionEntity execution = (ExecutionEntity) runtimeService.createProcessInstanceQuery().processInstanceId(taskId).singleResult();  
//		TaskQuery tq2 =taskService.createTaskQuery().executionId(execution.getId());
//		Task xx2 = tq2.singleResult();
		ActivitiNodeDTO curNode = new ActivitiNodeDTO();
		curNode.setProcessInstanceId(processInstanceId);
		if(task == null) {//节点end
			curNode.setNodeCode(CaseNodeConstant.WOEND.getCode());
			curNode.setNodeName("End");
			curNode.setProcessTaskId(null);
		}else {
			curNode.setNodeCode(task.getTaskDefinitionKey());
			curNode.setNodeName(task.getName());
			curNode.setProcessTaskId(task.getId());
		}
		return curNode;
	} 
	
	
//	public void firstDemo() {
//
//		//根据bpmn文件部署流程
//		Deployment deployment = repositoryService.createDeployment().addClasspathResource("demo2.bpmn").deploy();
//		//获取流程定义
//		ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().deploymentId(deployment.getId()).singleResult();
//		//启动流程定义，返回流程实例
//		ProcessInstance pi = runtimeService.startProcessInstanceById(processDefinition.getId());
//		String processId = pi.getId();
//		System.out.println("流程创建成功，当前流程实例ID："+processId);
//		
//		Task task=taskService.createTaskQuery().processInstanceId(processId).singleResult();
//		System.out.println("第一次执行前，任务名称："+task.getName());
//		taskService.complete(task.getId());
//
//		task = taskService.createTaskQuery().processInstanceId(processId).singleResult();
//		System.out.println("第二次执行前，任务名称："+task.getName());
//		taskService.complete(task.getId());
//
//		task = taskService.createTaskQuery().processInstanceId(processId).singleResult();
//		System.out.println("task为null，任务执行完毕："+task);
//	}
}
